/********************************************************************************
 *  
 *  Product: GSAOBKeyMatchesLib
 *  Description: A C# API for Responsing to Google Search Appliance OneBoxModule.
 *
 *  (c) Copyright 2007 
 *  
********************************************************************************/
using System;
using System.Collections;
using System.Text;
using System.IO;
using GSAOBKeyMatchesLib.Server;

namespace GSAOBKeyMatchesLib.Client
{
    /// <summary>
    /// Provides methods to create KeyMatch  objects and Response stream in XML format
    /// <para>Author Albert Ghukasyan</para>
    /// </summary>
    public class KeyMatch : Server.OneBoxProvider
    {
        #region Variables

        /// <summary>
        /// Contains List Of Keymatches with ExactMatch matchType specifier
        /// </summary>
        private Hashtable exactMatchHash = new Hashtable();

        /// <summary>
        /// Contains List Of Keymatches with PhraseMatch matchType specifier
        /// </summary>
        private Hashtable phraseMatchHash = new Hashtable();

        /// <summary>
        /// Contains List Of Keymatches with KeywordMatch matchType specifier
        /// </summary>
        private Hashtable keywordMatchHash = new Hashtable();

        /// <summary>
        /// Specifies Current Executing Assembly
        /// </summary>
        private System.Reflection.Assembly asm;

        /// <summary>
        /// Sepcifies Directory of Current Executing Assembly
        /// </summary>
        string Direcory = "";

        /// <summary>
        /// Builded Output Stream
        /// </summary>
        public string OutputStream = "";

        #endregion

        #region Contructor

        public KeyMatch()
        {
            asm = System.Reflection.Assembly.GetExecutingAssembly();
            Direcory = asm.CodeBase.Replace("file:///", "").Replace(asm.ManifestModule.Name, "");
        }

        #endregion

        #region Methods

        /// <summary>
        /// Log write
        /// </summary>
        /// <param name="sw">Stream writer Object</param>
        /// <param name="text">Text t log</param>
        private void log(StreamWriter sw, string text)
        {
            sw.WriteLine(text);
        }

        /// <summary>
        /// Initialize HashTables using specified keymatch file.
        /// </summary>
        /// <param name="URL_PREFIX">HTTP GET protocol Url received from GSA</param>
        /// <param name="KeymatchFile">Keymatch File from wich need to read KeyMatches</param>
        public void init(string URL_PREFIX, string KeymatchFile)
        {   
            StreamReader inputStream = null;

            try
            {
                inputStream = new StreamReader(this.Direcory + KeymatchFile);
                String l;
                while ((l = inputStream.ReadLine()) != null)
                {   
                    String[] tokens = l.Split(',');
                    if (tokens.Length < 4)
                    {
                        continue;
                    }
                    else
                    {
                        String keymatch = canonicalizeQuery(tokens[0]);
                        String matchType = tokens[1];
                        String url = tokens[2];
                        String matchString = tokens[3];
                        KMObject keymatchObj = new KMObject(url, matchString, keymatch);
                        if (matchType.Equals("ExactMatch"))
                        {
                            ArrayList list = exactMatchHash[keymatch] as ArrayList;
                            if (list == null)
                            {
                                list = new ArrayList();
                                list.Add(keymatchObj);
                                exactMatchHash.Add(keymatch, list);
                            }
                            else
                            {
                                list.Add(keymatchObj);
                                exactMatchHash[keymatch] = list;
                            }

                        }
                        if (matchType.Equals("PhraseMatch"))
                        {
                            ArrayList list = phraseMatchHash[keymatch] as ArrayList;
                            if (list == null)
                            {
                                list = new ArrayList();
                                list.Add(keymatchObj);
                                phraseMatchHash.Add(keymatch, list);
                            }
                            else
                            {
                                list.Add(keymatchObj);
                                phraseMatchHash[keymatch] = list;
                            }
                        }
                        if (matchType.Equals("KeywordMatch"))
                        {
                            String[] words = keymatch.Split(' ');
                            for (int i = 0; i < words.Length; i++)
                            {
                                ArrayList matches;
                                if (keywordMatchHash.ContainsKey(words[i]))
                                {
                                    matches = keywordMatchHash[words[i]] as ArrayList;
                                    matches.Add(keymatchObj);
                                    keywordMatchHash[words[i]] = matches;
                                }
                                else
                                {
                                    matches = new ArrayList();
                                    matches.Add(keymatchObj);
                                    keywordMatchHash.Add(words[i], matches);
                                }

                            }
                        }
                    }
                }
            }
            catch (IOException ioe)
            {
                
            }
            finally
            {
                if (inputStream != null)
                {
                    try
                    {
                        inputStream.Close();
                    }
                    catch (IOException ioe2) { }
                }
            }         
          
        }

        /// <summary>
        ///  Builds Canonical Query
        /// </summary>
        /// <param name="query">Query To Change to canonical</param>
        /// <returns>Builded Canonical Query</returns>
        private String canonicalizeQuery(String query)
        {
            String lowerQ = query.ToLower();
            String trimLowerQ = lowerQ.Trim();
            char[] qArray = new char[trimLowerQ.Length];
            StringBuilder canonicalQ = new StringBuilder("");
            qArray = trimLowerQ.ToCharArray();
            bool prevCharIsSpace = false;
            for (int i = 0; i < trimLowerQ.Length; i++)
            {
                char ch = qArray[i];
                if (Char.IsLetterOrDigit(ch))
                {
                    canonicalQ.Append(ch);
                    prevCharIsSpace = false;
                }
                else if (prevCharIsSpace == false)
                {
                    canonicalQ.Append(" ");
                    prevCharIsSpace = true;
                }
            }

            return canonicalQ.ToString().Trim();
        }

        /// <summary>
        /// Overriden getURL methode wich calls GetUlRL of base Class
        /// </summary>
        /// <returns>URL</returns>
        protected override string getURL()
        {
            return base.getURL();
        }

        /// <summary>
        /// Overriden processRequest methode wich calls processRequest of base Class
        /// </summary>
        /// <param name="request">Current HttpRequest object sended from GSA OneBox Module</param>
        /// <param name="URL_PREFIX">URL</param>
        /// <param name="KeymatchFile">KeyMatch File</param>
        public override void processRequest(System.Web.HttpRequest request, string URL_PREFIX, string KeymatchFile)
        {
            base.processRequest(request, URL_PREFIX, KeymatchFile);
        }

        /// <summary>
        /// Replaces some characters withh characters wich need to replace for saving in XML format
        /// </summary>
        /// <param name="str">String To Replace</param>
        /// <returns>Replaced String</returns>
        String escapeXML(String str)
        {
            return str.Replace("&", "&amp;").Replace("<", "&lt;").Replace(">", "&gt;").Replace("\"", "&quot;");
        }
                
        /// <summary>
        /// Overriden Method Of OneBoxPovider wich Builds OneBox Response
        /// </summary>
        /// <param name="apiMaj">Api Major Version</param>
        /// <param name="apiMin">Api Min Version</param>
        /// <param name="oneboxName">OneBox Name</param>
        /// <param name="dateTime">DateTime</param>
        /// <param name="ipAddr">Users IP address from wich was send Request</param>
        /// <param name="lang"></param>
        /// <param name="query">Query</param>
        /// <param name="matchGroups"></param>
        /// <returns></returns>
        protected override GSAOBKeyMatchesLib.Server.IOneBoxResults provideOneBoxResults(string apiMaj, string apiMin, string oneboxName, string dateTime, string ipAddr, string lang, string query, System.Collections.ArrayList matchGroups)
        {
            OneBoxResults res = new OneBoxResults();
            res.setProviderText("keymatch");

            #region Check api version for compatibility

            int apiVersion = ((Int32.Parse(apiMaj) << 0xFF) | (Int32.Parse(apiMin)));
            if (apiVersion < ((1 << 0xFF) | 0))
            {	// 1.0
                res.setFailure(FailureCodes.lookupFailure,
                               "OneBox API versions older than 1.0 not supported by provider");
                return res;
            }

            #endregion

            int matchCount = 0;
            String canonicalQuery = canonicalizeQuery(query);

            #region ExactMatch
 
            if (exactMatchHash.ContainsKey(canonicalQuery))
            {
                ArrayList list = exactMatchHash[canonicalQuery] as ArrayList;
                foreach (KMObject ko in list)
                {
                    if (res.canAddResult())
                    {
                        ModuleResult mr = new ModuleResult(escapeXML(ko.matchString), escapeXML(ko.url));
                        mr.addField(new FieldObject("matchType", "ExactMatch"));
                        mr.addField(new FieldObject("hashLookup", canonicalQuery));
                        mr.addField(new FieldObject("keymatch", ko.keymatch));
                        res.addResult(mr);
                        matchCount++;
                    }
                    else
                    {
                        break;
                    }
                }
            }

            #endregion

            #region KeywordMatch

            String[] words = canonicalQuery.Split(' ');
            ArrayList allMatchObjects = new ArrayList();
            for (int i1 = 0; i1 < words.Length; i1++)
            {
                if (keywordMatchHash.ContainsKey(words[i1]))
                {
                    ArrayList matchObjects = keywordMatchHash[words[i1]] as ArrayList;
                    foreach (KMObject ko in matchObjects)
                    {
                        if (!allMatchObjects.Contains(ko))
                        {
                            allMatchObjects.Add(ko);
                        }
                    }                    
                }
            }
            
            int i = 0;
           

            for (int j = 0; j < allMatchObjects.Count; j++)
            {
                if (res.canAddResult())
                {
                    KMObject ko = allMatchObjects[j] as KMObject;
                    ModuleResult mr = new ModuleResult(escapeXML(ko.matchString), escapeXML(ko.url));
                    mr.addField(new FieldObject("matchType", "Keyword"));
                    mr.addField(new FieldObject("hashLookup", canonicalQuery));
                    mr.addField(new FieldObject("keymatch", ko.keymatch));
                    res.addResult(mr);
                    matchCount++;
                }
            }

            #endregion

            #region PhraseMatch

            Phrases phrases = new Phrases(canonicalQuery);
            String phrase;
            for (; phrases.hasMoreElements(); )
            {
                phrase = (String)phrases.nextElement();
                if (phraseMatchHash.ContainsKey(phrase))
                {
                    ArrayList list = phraseMatchHash[phrase] as ArrayList;
                    foreach (KMObject ko in list)
                    {
                        if (res.canAddResult())
                        {
                            ModuleResult mr = new ModuleResult(escapeXML(ko.matchString), escapeXML(ko.url));
                            mr.addField(new FieldObject("matchType", "PhraseMatch"));
                            mr.addField(new FieldObject("hashLookup", phrase));
                            mr.addField(new FieldObject("keymatch", ko.keymatch));
                            res.addResult(mr);
                            matchCount++;
                        }
                        else
                        {
                            break;
                        }
                    }                   
                }
            }

            #endregion

            if (matchCount == 0)
            {
                res.setFailure(FailureCodes.lookupFailure, "No matches found for: " + canonicalQuery);
            }
            else
            {
                res.setResultsTitleLink("Matches: " + matchCount, escapeXML(this.getURL()));
            }

            OutputStream = res.Serialize();

            return res;
        }

        /// <summary>
        /// Overriden Method Of OneBoxPovider wich Builds OneBox Response
        /// </summary>
        /// <param name="apiMaj">Api Major Version</param>
        /// <param name="apiMin">Api Min Version</param>
        /// <param name="oneboxName">OneBox Name</param>
        /// <param name="dateTime">DateTime</param>
        /// <param name="ipAddr">Users IP address from wich was send Request</param>
        /// <param name="distinguishedName"></param>
        /// <param name="lang"></param>
        /// <param name="query">Query</param>
        /// <param name="matchGroups"></param>
        /// <returns></returns>
        protected override GSAOBKeyMatchesLib.Server.IOneBoxResults provideOneBoxResults(string apiMaj, string apiMin, string oneboxName, string dateTime, string ipAddr, string distinguishedName, string lang, string query, System.Collections.ArrayList matchGroups)
        {
            return this.provideOneBoxResults(apiMaj,apiMin,oneboxName,dateTime,ipAddr,lang,query,matchGroups);
        }

        /// <summary>
        /// Overriden Method Of OneBoxPovider wich Builds OneBox Response
        /// </summary>
        /// <param name="apiMaj">Api Major Version</param>
        /// <param name="apiMin">Api Min Version</param>
        /// <param name="oneboxName">OneBox Name</param>
        /// <param name="dateTime">DateTime</param>
        /// <param name="ipAddr">Users IP address from wich was send Request</param>
        /// <param name="userName"></param>
        /// <param name="password"></param>
        /// <param name="lang"></param>
        /// <param name="query">Query</param>
        /// <param name="matchGroups"></param>
        /// <returns></returns>
        protected override GSAOBKeyMatchesLib.Server.IOneBoxResults provideOneBoxResults(string apiMaj, string apiMin, string oneboxName, string dateTime, string ipAddr, string userName, string password, string lang, string query, System.Collections.ArrayList matchGroups)
        {
            return this.provideOneBoxResults(apiMaj, apiMin, oneboxName, dateTime, ipAddr, lang, query, matchGroups);
        }

        /// <summary>
        /// Overriden Method Of OneBoxPovider wich Builds OneBox Response
        /// </summary>
        /// <param name="apiMaj">Api Major Version</param>
        /// <param name="apiMin">Api Min Version</param>
        /// <param name="oneboxName">OneBox Name</param>
        /// <param name="dateTime">DateTime</param>
        /// <param name="ipAddr">Users IP address from wich was send Request</param>
        /// <param name="ssoCookie"></param>
        /// <param name="lang"></param>
        /// <param name="query"></param>
        /// <param name="matchGroups"></param>
        /// <returns></returns>
        protected override GSAOBKeyMatchesLib.Server.IOneBoxResults provideOneBoxResults(string apiMaj, string apiMin, string oneboxName, string dateTime, string ipAddr, System.Web.HttpCookie ssoCookie, string lang, string query, System.Collections.ArrayList matchGroups)
        {
            return this.provideOneBoxResults(apiMaj, apiMin, oneboxName, dateTime, ipAddr, lang, query, matchGroups);
        }

        #endregion
    }
}
